#include "FrameQueue.h"

FrameQueue::FrameQueue() :
    rindex(0),
    windex(0),
    size(0),
    rindex_shown(0),
    pktq(nullptr)
{

}

FrameQueue::~FrameQueue()
{

}

int FrameQueue::frame_queue_init(shared_ptr<PacketQueue> pktq, int max, bool keep_last)
{
    uint32_t i;
    memset(queue, 0, sizeof(queue));
    this->pktq = pktq;
    this->maxSize = FFMIN(max, FRAME_QUEUE_SIZE);
    this->keep_last = keep_last;
    for (i = 0; i < this->maxSize; i++)
        if (!(queue[i].frame = av_frame_alloc()))
            return AVERROR(ENOMEM);
    return 0;
}

void FrameQueue::frame_queue_destory()
{
    uint32_t i;
    for (i = 0; i < maxSize; i++) {
        Frame *vp = &queue[i];

        // 释放对vp->frame中的数据缓冲区的引用，注意不是释放frame对象本身
        frame_queue_unref_item(vp);

        // 释放vp->frame对象
        av_frame_free(&vp->frame);
    }
}

void FrameQueue::frame_queue_signal()
{
    lock_guard<mutex> lock(_mutex);
    _condition.notify_all();
}

FrameQueue::Frame *FrameQueue::frame_queue_peek_writable()
{
    /* wait until we have space to put a new frame */
    unique_lock<mutex> lock(_mutex);
    while (size >= maxSize && !pktq->packet_queue_isAbort()) {		// 检查是否需要退出
        _condition.wait(lock);
    }

    if (pktq->packet_queue_isAbort())				// 检查是否需要退出
        return nullptr;

    return &queue[windex];
}

void FrameQueue::frame_queue_push()
{
    if (++windex == maxSize)
        windex = 0;
    lock_guard<mutex> lock(_mutex);
    size++;
    _condition.notify_all();    // 当frame_queue_peek_readable在等待时则可以唤醒
}

FrameQueue::Frame *FrameQueue::frame_queue_peek_readable()
{
    /* wait until we have a readable a new frame */
    unique_lock<mutex> lock(_mutex);
    while (size - rindex_shown <= 0 &&	// 如果有keep_last机制则忽略last帧
           !pktq->packet_queue_isAbort()) {
        _condition.wait(lock);
    }

    if (pktq->packet_queue_isAbort())
        return nullptr;

    return &queue[(rindex + rindex_shown) % maxSize];
}

void FrameQueue::frame_queue_next()
{
    // 当keep_last为1, rindex_show为0时不去更新rindex,也不释放当前frame
    if (keep_last && !rindex_shown) {
        rindex_shown = 1;           // 第一次调用置为1
        return;
    }
    frame_queue_unref_item(&queue[rindex]);
    if (++rindex == maxSize)
        rindex = 0;
    lock_guard<mutex> lock(_mutex);
    size--;
    _condition.notify_all();
}

void FrameQueue::frame_queue_unref_item(FrameQueue::Frame *vp)
{
    av_frame_unref(vp->frame);
}
